#include <QtWidgets/QMessageBox>
#include <QtWidgets/QFileDialog>
#include <fstream>
#include "NearDialog.h"

NearDialog::NearDialog(QWidget *parent)
{
	ui.setupUi(this);
	this->setFixedSize(this->width(), this->height());

	connect(ui.user, SIGNAL(clicked()), this, SLOT(fn_CheckRotation()));
	connect(ui.mesh, SIGNAL(clicked()), this, SLOT(fn_CheckRotation()));
	connect(ui.minX, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumX(QString)));
	connect(ui.maxX, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumX(QString)));
	connect(ui.dx,   SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumX(QString)));
	connect(ui.minY, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumY(QString)));
	connect(ui.maxY, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumY(QString)));
	connect(ui.dy,   SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumY(QString)));
	connect(ui.minZ, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumZ(QString)));
	connect(ui.maxZ, SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumZ(QString)));
	connect(ui.dz,   SIGNAL(textEdited(QString)), this, SLOT(fn_UpdateNumZ(QString)));
	connect(ui.open, SIGNAL(clicked()), this, SLOT(fn_GetMshFile()));
	connect(ui.cancel, SIGNAL(clicked()), this, SLOT(reject()));
	connect(ui.apply,  SIGNAL(clicked()), this, SLOT(fn_AddNearApply()));
}

void NearDialog::fn_AddNearSet(std::vector<PreNearSet> &vNear, QTreeWidgetItem *pRoot)
{
	setWindowTitle("Add Near Field Surface");

	m_vNear = &vNear;
	m_pNearNode = pRoot;

	std::string sName;
	QString str;
	int i = 1;
	while (true) {
		str = "Near" + QString::number(i);
		sName = str.toStdString();
		if (!fn_IsNameExist(sName, -1)) {
			break;
		}
		++i;
	}
	ui.name->setText(str);
	ui.meshFile->setText("");
	ui.apply->setEnabled(true);

	disconnect(ui.ok, 0, 0, 0);
	connect(ui.ok, SIGNAL(clicked()), this, SLOT(fn_AddNearClose()));
	this->show();
}

void NearDialog::fn_EditNearSet(std::vector<PreNearSet> &vNear, QTreeWidgetItem *pNode, int &iIndx)
{
	setWindowTitle("Edit Near Field Surface");
	ui.name->setText(QString::fromStdString(vNear[iIndx].sName));
	ui.apply->setDisabled(true);
	if (vNear[iIndx].bUserType) {
		ui.user->setChecked(true);
		ui.minX->setText(QString::number(vNear[iIndx].dMinX));
		ui.maxX->setText(QString::number(vNear[iIndx].dMaxX));
		ui.minY->setText(QString::number(vNear[iIndx].dMinY));
		ui.maxY->setText(QString::number(vNear[iIndx].dMaxY));
		ui.minZ->setText(QString::number(vNear[iIndx].dMinZ));
		ui.maxZ->setText(QString::number(vNear[iIndx].dMaxZ));
		ui.dx->setText(QString::number(vNear[iIndx].dX));
		ui.dy->setText(QString::number(vNear[iIndx].dY));
		ui.dz->setText(QString::number(vNear[iIndx].dZ));
		int iNumX = 1;
		int iNumY = 1;
		int iNumZ = 1;
		if (fabs(vNear[iIndx].dX) > 1.0e-8) {
			iNumX = (vNear[iIndx].dMaxX - vNear[iIndx].dMinX) / vNear[iIndx].dX + 1;
		}
		if (fabs(vNear[iIndx].dY) > 1.0e-8) {
			iNumY = (vNear[iIndx].dMaxY - vNear[iIndx].dMinY) / vNear[iIndx].dY + 1;
		}
		if (fabs(vNear[iIndx].dZ) > 1.0e-8) {
			iNumZ = (vNear[iIndx].dMaxZ - vNear[iIndx].dMinZ) / vNear[iIndx].dZ + 1;
		}
		ui.numX->setText(QString::number(iNumX));
		ui.numY->setText(QString::number(iNumY));
		ui.numZ->setText(QString::number(iNumZ));
	} else {
		ui.mesh->setChecked(true);
		ui.meshFile->setText(QString::fromStdString(vNear[iIndx].sFile));
	}
	fn_CheckRotation();

	m_vNear = &vNear;
	m_pNearNode = pNode;
	m_iCur = iIndx;

	disconnect(ui.ok, 0, 0, 0);
	connect(ui.ok, SIGNAL(clicked()), this, SLOT(fn_EditNearClose()));
	this->show();
}

void NearDialog::fn_CheckRotation()
{
	if (ui.user->isChecked()) {
		ui.meshFile->setDisabled(true);
		ui.open->setDisabled(true);
		ui.minX->setEnabled(true);
		ui.maxX->setEnabled(true);
		ui.minY->setEnabled(true);
		ui.maxY->setEnabled(true);
		ui.minZ->setEnabled(true);
		ui.maxZ->setEnabled(true);
		ui.dx->setEnabled(true);
		ui.dy->setEnabled(true);
		ui.dz->setEnabled(true);
	} else {
		ui.meshFile->setEnabled(true);
		ui.open->setEnabled(true);
		ui.minX->setDisabled(true);
		ui.maxX->setDisabled(true);
		ui.minY->setDisabled(true);
		ui.maxY->setDisabled(true);
		ui.minZ->setDisabled(true);
		ui.maxZ->setDisabled(true);
		ui.dx->setDisabled(true);
		ui.dy->setDisabled(true);
		ui.dz->setDisabled(true);
	}
}

void NearDialog::fn_AddNearClose()
{
	PreNearSet near;

	fn_SetNearValue(near);
	if (!fn_IsNameExist(near.sName, -1)) {
		m_vNear->push_back(near);

		// insert item in the tree
		QTreeWidgetItem* pSon = new QTreeWidgetItem();
		pSon->setText(0, QString::fromStdString(near.sName));
		m_pNearNode->addChild(pSon);

		this->reject();
	} else {
		QMessageBox::information(this, "Warn", "The name of near field set has been exist.");
	}
}

void NearDialog::fn_AddNearApply()
{
	PreNearSet near;

	fn_SetNearValue(near);
	if (!fn_IsNameExist(near.sName, -1)) {
		m_vNear->push_back(near);
		// insert item in the tree
		auto pSon = new QTreeWidgetItem();
		pSon->setText(0, QString::fromStdString(near.sName));
		m_pNearNode->addChild(pSon);

		std::string sName;
		QString str;
		int i = 1;
		while (true) {
			str = "Near" + QString::number(i);
			sName = str.toStdString();
			if (!fn_IsNameExist(sName, -1)) {
				break;
			}
			++i;
		}
		ui.name->setText(str);
	} else {
		QMessageBox::information(this, "Warn", "The name of near field set has been exist.");
	}
}

void NearDialog::fn_EditNearClose()
{
	std::string sName = ui.name->text().toStdString();

	if (!fn_IsNameExist(sName, m_iCur)) {
		fn_SetNearValue((*m_vNear)[m_iCur]);
		m_pNearNode->setText(0, QString::fromStdString(sName));
		this->reject();
	} else {
		QMessageBox::information(this, "Warn", "The name of near field set has been exist.");
	}
}

void NearDialog::fn_UpdateNumX(QString arg)
{
	double dMaxX = ui.maxX->text().toDouble() - ui.minX->text().toDouble();
	double dX = ui.dx->text().toDouble();
	if (fabs(dX) > 1.0e-8) {
		int iNumX = dMaxX / dX + 1;
		if (iNumX < 0) iNumX = 0;
		ui.numX->setText(QString::number(iNumX));
	} else {
		ui.numX->setText("1");
	}
}

void NearDialog::fn_UpdateNumY(QString arg)
{
	double dMaxY = ui.maxY->text().toDouble() - ui.minY->text().toDouble();
	double dY = ui.dy->text().toDouble();
	if (fabs(dY) > 1.0e-8) {
		int iNumY = dMaxY / dY + 1;
		if (iNumY < 0) iNumY = 0;
		ui.numY->setText(QString::number(iNumY));
	} else {
		ui.numY->setText("1");
	}
}

void NearDialog::fn_UpdateNumZ(QString arg)
{
	double dMaxZ = ui.maxZ->text().toDouble() - ui.minZ->text().toDouble();
	double dZ = ui.dz->text().toDouble();
	if (fabs(dZ) > 1.0e-8) {
		int iNumZ = dMaxZ / dZ + 1;
		if (iNumZ < 0) iNumZ = 0;
		ui.numZ->setText(QString::number(iNumZ));
	} else {
		ui.numZ->setText("1");
	}
}

void NearDialog::fn_GetMshFile()
{
	auto fd = QFileDialog::getOpenFileName(this, "Open mesh file", "", "Mesh File (*.msh)");
	if(!fd.isEmpty()) ui.meshFile->setText(fd);
}

void NearDialog::fn_SetNearValue(PreNearSet &near)
{
	near.sName = ui.name->text().toStdString();
	if (ui.user->isChecked()) {
		near.bUserType = true;
		near.dMinX = ui.minX->text().toDouble();
		near.dMaxX = ui.maxX->text().toDouble();
		near.dMinY = ui.minY->text().toDouble();
		near.dMaxY = ui.maxY->text().toDouble();
		near.dMinZ = ui.minZ->text().toDouble();
		near.dMaxZ = ui.maxZ->text().toDouble();
		near.dX = ui.dx->text().toDouble();
		near.dY = ui.dy->text().toDouble();
		near.dZ = ui.dz->text().toDouble();
	} else {
		near.bUserType = false;
		near.sFile = ui.meshFile->text().toStdString();
		if (!fn_LoadNearMesh(near)) {
			QMessageBox::information(this, "Warn", "Load near field mesh failed.");
		}
	}
}

bool NearDialog::fn_LoadNearMesh(PreNearSet &near)
{
	if (near.sFile.empty()) {
		return false;
	}

	std::ifstream fp(near.sFile);
	if (!fp.is_open()) {
		return false;
	}

	near.vR.clear();
	near.vvTria.clear();
	near.vvQuad.clear();
	std::string sLine;
	getline(fp, sLine);
	int iNode = 0;
	if (std::string::npos != sLine.find("Triangle")) {
		iNode = 3;
	} else if(std::string::npos != sLine.find("Quadrilateral")){
		iNode = 4;
	} else {
		fp.close();
		return false;
	}
	getline(fp, sLine);
	Node r;
	int i;
	while (fp >> i >> r.x >> r.y >> r.z) {
		near.vR.push_back(r);
	}
	fp.clear();   // clear the failed state
	i = 100;
	while (i > 0) {
		getline(fp, sLine);
		if (sLine == "Elements") {
			break;
		}
		--i;
	}
	if (i < 1) return false;

	if (iNode == 3) {
		auto v = std::vector<int>(3);
		while (fp >> i >> v[0] >> v[1] >> v[2]) {
			near.vvTria.push_back(v);
		}
	} else {
		auto v = std::vector<int>(4);
		while (fp >> i >> v[0] >> v[1] >> v[2] >> v[3]) {
			near.vvQuad.push_back(v);
		}
	}
	fp.close();

	return true;
}

bool NearDialog::fn_IsNameExist(const std::string &sName, int iSkip)
{
	for (unsigned i = 0; i < (*m_vNear).size(); ++i) {
		if (i == iSkip) continue;

		if ((*m_vNear)[i].sName == sName) {
			return true;
		}
	}
	return false;
}
